package com.kanesoft.android.gdx.control;

import java.util.List;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Gdx2DPixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.kanesoft.android.gdx.actors.ActorEventsBroadcast;
import com.kanesoft.android.gdx.actors.ActorObject;
import com.kanesoft.android.gdx.actors.fight.FightActorBride;
import com.kanesoft.android.gdx.actors.fight.FightActorRyu;
import com.kanesoft.android.gdx.backgrounds.Background;

public class WorldControl implements Screen {

    private World world;

    private final float CAMERA_VIEW_SIZE_X = 28;
    private final float CAMERA_VIEW_SIZE_Y = 20;

    private final float ORIGINAL_WIDTH_IN_PIXEL = 1024;
    private final float ORIGINAL_HEIGHT_IN_PIXEL = 640;

    private FightActorBride ryu;
    private FightActorRyu ryu2;
    private ActorEventsBroadcast actorEventsBroadcast;

    private Fixture groundSensorFixture;

    private OrthographicCamera camera;
    private Box2DDebugRenderer renderer;

    private FightControl fightControl;

    private Background backPicture;
    private Background backTree;
    private float limitRightX, limitLeftX;

    public WorldControl() {
        world = new World(new Vector2(0, -40), true);
        renderer = new Box2DDebugRenderer();
        actorEventsBroadcast = new ActorEventsBroadcast();
        camera = new OrthographicCamera(CAMERA_VIEW_SIZE_X, CAMERA_VIEW_SIZE_Y);

        createWorld();
    }

    /**
     * Retuns an instance of the current World
     * 
     * @return
     */
    public World getWorld() {
        return world;
    }

    /**
     * Returns the instance of the ActorEventsBroadcast handler
     * 
     * @return
     */
    public ActorEventsBroadcast getEventsBroadcast() {
        return actorEventsBroadcast;
    }

    /**
     * Returns the camera point in X axis into a screen point
     * 
     * @param glPosX
     * @return pixel position in X
     */
    public int convertGLPosXToPixel(float glPosX) {
        glPosX = (glPosX + (CAMERA_VIEW_SIZE_X / 2));

        return Math.round((glPosX * Gdx.graphics.getWidth()) / CAMERA_VIEW_SIZE_X);
    }

    /**
     * Returns the camera point in Y axis into a screen point
     * 
     * @param glPosY
     * @return pixel position in Y
     */
    public int convertGLPosYToPixel(float glPosY) {
        glPosY = (glPosY + (CAMERA_VIEW_SIZE_Y / 2));

        return Math.round((glPosY * Gdx.graphics.getHeight()) / CAMERA_VIEW_SIZE_Y);
    }

    /**
     * Returns the pixel according camera point in X axis
     * 
     * @param pixelX
     * @return float position in X
     */
    public float convertPixelXGLPosX(int pixelX) {

        float glPosX = (((float) pixelX / Gdx.graphics.getWidth()) * (float) CAMERA_VIEW_SIZE_X)
                - (float) (CAMERA_VIEW_SIZE_X / (float) 2);

        return glPosX;
    }

    /**
     * Returns the pixel according camera point in Y axis
     * 
     * @param pixelY
     * @return float position in Y
     */
    public float convertPixelYGLPosY(int pixelY) {
        float glPosY = (((float) pixelY / Gdx.graphics.getHeight()) * (float) CAMERA_VIEW_SIZE_Y)
                - ((float) CAMERA_VIEW_SIZE_Y / (float) 2);

        return glPosY;
    }

    /**
     * Gives the width scale needed to make any bitmap match the current Camera
     * Size
     * 
     * @return X scale value
     */
    public float getScreenScaleFactorWidth() {
        return Gdx.graphics.getWidth() / ORIGINAL_WIDTH_IN_PIXEL;
    }

    /**
     * Gives the height scale needed to make any bitmap match the current Camera
     * Size
     * 
     * @return Y scale value
     */
    public float getScreenScaleFactorHeigth() {
        return Gdx.graphics.getHeight() / ORIGINAL_HEIGHT_IN_PIXEL;
    }

    private void createWorld() {

        Body ground = createFloorBox(BodyType.StaticBody, CAMERA_VIEW_SIZE_X * 1.5f, 0.01f, 1);
        ground.setTransform(0, -8, 0);

        Body limitRight = createBox(BodyType.StaticBody, 0.01f, CAMERA_VIEW_SIZE_Y, 1);
        limitRight.setTransform((CAMERA_VIEW_SIZE_X / 2 * 1.5f) - 1, CAMERA_VIEW_SIZE_Y / 2, 0);
        limitRightX = limitRight.getPosition().x;

        Body limitLeft = createBox(BodyType.StaticBody, 0.01f, CAMERA_VIEW_SIZE_Y, 1);
        limitLeft.setTransform(-(CAMERA_VIEW_SIZE_X / 2 * 1.5f) + 1, CAMERA_VIEW_SIZE_Y / 2, 0);
        limitLeftX = limitLeft.getPosition().x;

        backPicture = new Background("data/capela.png", -150, 0, 960, 640, 1.2f, 1, 0.02f, 1);
        backTree =  new Background("data/tree.png", 130, 50, 817, 978, 0.5f, 0.5f, 0.5f, 1);

        ryu = new FightActorBride(1, 1, getScreenScaleFactorWidth(), this);
        ryu.getActorBody().setTransform(-10.0f, -7.98f, 0);

        ryu2 = new FightActorRyu(1, 1, getScreenScaleFactorWidth(), this);
        ryu2.getActorBody().setTransform(10.0f, -7.98f, 0);
        ryu2.setHorizontalFlip(true);

        fightControl = new FightControl(ryu, ryu2, false);

    }

    private Body createBox(BodyType type, float width, float height, float density) {
        BodyDef def = new BodyDef();
        def.type = type;
        Body box = world.createBody(def);

        PolygonShape poly = new PolygonShape();
        poly.setAsBox(width, height);
        box.createFixture(poly, density).setFriction(0f);
        poly.dispose();

        return box;
    }

    private Body createFloorBox(BodyType type, float width, float height, float density) {
        BodyDef def = new BodyDef();
        def.type = type;
        Body box = world.createBody(def);

        PolygonShape poly = new PolygonShape();
        poly.setAsBox(width, height);
        groundSensorFixture = box.createFixture(poly, density);
        poly.dispose();
        groundSensorFixture.setFriction(1);
        return box;
    }

    @Override
    public void resume() {

    }

    @Override
    public void render(float deltaTime) {
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        

        float actorScreenLimitRight = ryu.getActorBody().getPosition().x - (camera.position.x);
        float actorScreenLimitLeft = ryu.getActorBody().getPosition().x - (camera.position.x);
        
        if (ryu.getHorizontalFlip() == false) {
            actorScreenLimitLeft = ryu.getActorBody().getPosition().x - (camera.position.x - CAMERA_VIEW_SIZE_X / 4f);
        } else {
            actorScreenLimitRight = ryu.getActorBody().getPosition().x - (camera.position.x + CAMERA_VIEW_SIZE_X / 4f);
        }

        if ((actorScreenLimitRight > 0) && (((camera.position.x + CAMERA_VIEW_SIZE_X / 2) < limitRightX))) camera.position.x += actorScreenLimitRight;
        if ((actorScreenLimitLeft < 0) && (((camera.position.x - CAMERA_VIEW_SIZE_X / 2) > limitLeftX))) camera.position.x += actorScreenLimitLeft;

        camera.update();
        camera.apply(Gdx.gl10);

        renderer.render(world, camera.combined);

        float camPosX = (Gdx.graphics.getWidth() / 2) - convertGLPosXToPixel(camera.position.x);

        actorEventsBroadcast.updateActorsPositions();

        objectsHighLevelCollisionCheck();

        backPicture.drawBackground(deltaTime, camPosX, 0);
        backTree.drawBackground(deltaTime, camPosX, 0);

        fightControl.updateFightControl();

        actorEventsBroadcast.updateEvents(deltaTime, camPosX, 0);

        world.step(deltaTime, 4, 4);
    }

    /**
     * Use this method to check collisions between OBJECTS (not sprites) As we
     * are only checking sprites collision, this function is not used by now.
     */
    public void objectsHighLevelCollisionCheck() {
        List<Contact> contactList = world.getContactList();
        for (int i = 0; i < contactList.size(); i++) {
            Contact contact = contactList.get(i);

            if (contact.isTouching()) {

                if ((contact.getFixtureA().getUserData() != null) && (contact.getFixtureB().getUserData() != null)) {
                    ActorObject actObjA = (ActorObject) contact.getFixtureA().getUserData();
                    ActorObject actObjB = (ActorObject) contact.getFixtureB().getUserData();
                    actObjA.handleActorsCollision(actObjB);
                    actObjB.handleActorsCollision(actObjA);
                }
            }
        }
    }

    public boolean isObjectOnTheGround(ActorObject actor) {
        List<Contact> contactList = world.getContactList();
        for (int i = 0; i < contactList.size(); i++) {
            Contact contact = contactList.get(i);

            if (contact.isTouching()) {

                if ((contact.getFixtureA() == groundSensorFixture)
                        && (contact.getFixtureB() == actor.getSensorFixture())) { return true; }
                if ((contact.getFixtureB() == groundSensorFixture)
                        && (contact.getFixtureA() == actor.getSensorFixture())) { return true; }
            }
        }
        return false;
    }

    @Override
    public void resize(int width, int height) {

    }

    @Override
    public void pause() {

    }

    @Override
    public void dispose() {

    }

    /**
     * Convert World coordinates coordinates to Pixel coordinates
     * 
     * @param x
     * @param y
     * @return
     */
    public Vector2 convertWorldPointToPixel(float x, float y) {
        /*
         * Vector3 pixel = new Vector3(x, y, 0); camera.project(pixel); return
         * new Vector2(pixel.x, pixel.y);
         */

        return new Vector2(convertGLPosXToPixel(x), convertGLPosYToPixel(y));
    }

    @Override
    public void show() {
        // TODO Auto-generated method stub

    }

    @Override
    public void hide() {
        // TODO Auto-generated method stub

    }

}